#include "Channel.h"
#include "EventLoop.h"
#include "Logger.h"

#include <sys/epoll.h>

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = EPOLLIN | EPOLLPRI;
const int Channel::kWriteEvent = EPOLLOUT;

Channel::Channel(EventLoop *loop, int fd)
    : loop_(loop)
    , fd_(fd)
    , events_(0)
    , revents_(0)
    , index_(-1)
    , tied_(false)
{
}

Channel::~Channel()
{
}

// fd得到Poller通知以后，处理事件的
/**
 * 当要执行事件处理函数时，先把弱指针通过lock()提升为shared_ptr，只有当弱指针指向的对象存在时才会成功。其实就是判断指向的对象是否存在
 * 提升成功后再去调用事件处理函数。
 * 这可以确保在Channel对象生命周期内，对应的TcpConnection对象也不会被销毁，
 * 因为channel的事件处理函数都是TcpConnection的成员函数，都有this参数，TcpConnection销毁后再调用this出错
 *
 * 提升为shared_ptr后，底层计数器会+1，那么至少再出这里的shared_ptr生命周期之前，TcpConnection对象不会被销毁。
 */
void Channel::handleEvent(Timestamp receiveTime)
{
    if (tied_)
    {
        std::shared_ptr<void> guard = tie_.lock();
        if (guard)
        {
            handleEventWithGuard(receiveTime);
        }
    }
    else
    {
        handleEventWithGuard(receiveTime);
    }
}

// 让弱指针指向对应的TcpConnection对象
void Channel::tie(const std::shared_ptr<void> &obj)
{
    tie_ = obj;
    tied_ = true;
}

void Channel::remove()
{
    loop_->removeChannel(this); // channel和poller无法直接沟通，需要通过Eventloop
}
void Channel::update()
{
    loop_->updateChannel(this);
}

void Channel::handleEventWithGuard(Timestamp receiveTime)
{
    LOG_DEBUG("%s:%d:channel handleEvent revents:%d, 属于loop: %p", __FILE__, __LINE__, revents_,ownerLoop());

    // 因为我们采用LT模式，当连接被挂断后，可能缓冲区还有数据每读完，EPOLLIN便就绪，
    // 故当EPOLLHUP满足且EPOLLIN不满足时，再执行closeCallback_()
    if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN))     //TcpConnection::shutdown被调用关闭写端时发生
    {
        if (closeCallback_)
            closeCallback_();
    }

    if (revents_ & EPOLLERR)
    {
        if (errorCallback_)
            errorCallback_();
    }

    if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP))
    {
        if (readCallback_)
            readCallback_(receiveTime);
    }

    if (revents_ & EPOLLOUT)
    {
        if (writeCallback_)
            writeCallback_();
    }

    if(eventCallback_)
        eventCallback_();
}
